package logs

import (
	"fmt"
	"gitee.com/captials-team/ubdframe/src/domain/configstc"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"strings"
)

// DefaultZapLog default logger
type DefaultZapLog struct {
	config *ZapLogConfig //已废弃,请使用cnf
	cnf    *configstc.LogConfig
	zapLog *zap.Logger

	logHelper
}

// ZapLogConfig 日志配置，已经废弃，请使用config.LogConfig
type ZapLogConfig struct {
	SyncStdOut bool //是否同步输出至标准输出
	MaxSize    int  // 每个日志文件保存的大小 单位:M
	MaxAge     int  // 文件最多保存多少天
	MaxBackups int  // 日志文件最多保存多少个备份
	Compress   bool // 是否压缩

	LogLevel string //日志级别
}

func NewZapLog(name string, logPath string, cs ...*configstc.LogConfig) *DefaultZapLog {
	c := configstc.DefiLogConfig()
	c.LogPath = logPath
	if len(cs) > 0 && cs[0] != nil {
		c = cs[0]
	}

	l := &DefaultZapLog{
		cnf: c,
	}

	hook := lumberjack.Logger{
		Filename:   l.lLogPath(l.cnf), // 日志文件路径
		MaxSize:    c.LogMaxSize,      // 每个日志文件保存的大小 单位:M
		MaxAge:     c.LogMaxAge,       // 文件最多保存多少天
		MaxBackups: c.LogMaxBackups,   // 日志文件最多保存多少个备份
		Compress:   c.LogCompress,     // 是否压缩
	}
	encoderConfig := zapcore.EncoderConfig{
		MessageKey:     "msg",
		LevelKey:       "level",
		TimeKey:        "time",
		NameKey:        "logger",
		CallerKey:      "file",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder, // 短路径编码器
		EncodeName:     zapcore.FullNameEncoder,
	}
	// 设置日志级别
	atomicLevel := zap.NewAtomicLevel()
	atomicLevel.SetLevel(levelToZapLevel(l.lLogLevel(c)))
	var writes = []zapcore.WriteSyncer{zapcore.AddSync(&hook)}
	// 如果是开发环境，同时在控制台上也输出
	if c.LogSyncStdOut {
		writes = append(writes, zapcore.AddSync(os.Stdout))
	}

	core := zapcore.NewCore(
		zapcore.NewConsoleEncoder(encoderConfig),
		zapcore.NewMultiWriteSyncer(writes...),
		atomicLevel,
	)

	// 构造日志
	l.zapLog = zap.New(core,
		zap.AddCaller(),                       // 开启开发模式，堆栈跟踪
		zap.AddCallerSkip(2+l.lCallerSkip(c)), //二次封装后需要修改调用堆栈文件位置
		zap.Development(),                     // 开启文件及行号
		zap.Fields(zap.String("Name", name)),  // 设置初始化字段
	)
	l.Info("log init success")
	return l
}

func NewWithZapLog(zlog *zap.Logger) *DefaultZapLog {
	l := &DefaultZapLog{}

	// 构造日志
	l.zapLog = zlog
	l.Info("log init success")
	return l
}

var ZapLogLevelMap = map[LogLevel]zapcore.Level{
	DebugLog: zapcore.DebugLevel,
	InfoLog:  zapcore.InfoLevel,
	WarnLog:  zapcore.WarnLevel,
	ErrorLog: zapcore.ErrorLevel,
	PanicLog: zapcore.PanicLevel,
	FatalLog: zapcore.FatalLevel,
}

func levelToZapLevel(level string) zapcore.Level {
	return ZapLogLevelMap[NameToLogLevel(strings.ToUpper(level))]
}

func (l DefaultZapLog) Write(p []byte) (n int, err error) {
	l.zapLog.Info(string(p))
	return len(p), nil
}

func (l *DefaultZapLog) Caller(offset int) *DefaultZapLog {
	return &DefaultZapLog{
		config: l.config,
		cnf:    l.cnf,
		zapLog: l.zapLog.WithOptions(
			zap.AddCallerSkip(offset), //二次封装后需要修改调用堆栈文件位置
		),
	}
}

func (l *DefaultZapLog) CallerSkip(offset int) ILog {
	return l.Caller(offset)
}

func (l *DefaultZapLog) Debug(format string, params ...interface{}) {
	if len(params) <= 0 {
		l.zapLog.Debug(format)
		return
	}
	l.zapLog.Debug(fmt.Sprintf(format, params...))
	return
}

func (l *DefaultZapLog) Info(format string, params ...interface{}) {
	if len(params) <= 0 {
		l.zapLog.Info(format)
		return
	}
	l.zapLog.Info(fmt.Sprintf(format, params...))
	return
}

func (l *DefaultZapLog) Warn(format string, params ...interface{}) {
	if len(params) <= 0 {
		l.zapLog.Warn(format)
		return
	}
	l.zapLog.Warn(fmt.Sprintf(format, params...))
	return
}

func (l *DefaultZapLog) Error(format string, params ...interface{}) {
	if len(params) <= 0 {
		l.zapLog.Error(format)
		return
	}
	l.zapLog.Error(fmt.Sprintf(format, params...))
	return
}

func (l *DefaultZapLog) Fatal(format string, params ...interface{}) {
	if len(params) <= 0 {
		l.zapLog.Fatal(format)
		return
	}
	l.zapLog.Fatal(fmt.Sprintf(format, params...))
	return
}

func (l *DefaultZapLog) Panic(format string, params ...interface{}) {
	if len(params) <= 0 {
		l.zapLog.Panic(format)
		return
	}
	l.zapLog.Panic(fmt.Sprintf(format, params...))
	return
}

func (l *DefaultZapLog) Ctl(t bool) ILog {
	if !t {
		return _nullLog
	}
	return l
}
